﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WgFrame.Net.Peer;

namespace WgFrame.Net
{
    public class WgPeer
    {
        private readonly object sendOutgoingLockObject = new object();

        private readonly object dispatchLockObject = new object();

        private readonly object enqueueLock = new object();

        internal PeerBase peerBase;

        public DebugLevel DebugOut = DebugLevel.ALL;

        public int TimePingInterval = 2000;

        public int DisconnectTimeout = 10000;

        public IWgPeerListener Listener { get; protected set; }

        public ConnectionProtocol TransportProtocol { get; set; }

        public bool IsEncryptionAvailable { get { return peerBase.isEncryptionAvailable; } }

        public PeerStateValue PeerState
        {
            get
            {
                if (peerBase.peerConnectionState == ConnectionStateValue.Connected && !peerBase.ApplicationIsInitialized)
                {
                    return PeerStateValue.InitializingApplication;
                }
                return (PeerStateValue)peerBase.peerConnectionState;
            }
        }

        public WgPeer() 
        {
            this.CreatePeerBase();
        }

        public WgPeer(IWgPeerListener listener) : this()
        {
            this.Listener = listener;
        }

        public virtual bool Connect(string serverAddress, string applicationName, object custom)
        {
            this.TransportProtocol = GetConnectionProtocol(serverAddress);
            lock (dispatchLockObject)
            {
                lock (sendOutgoingLockObject)
                {
                    CreatePeerBase();
                    if (peerBase == null)
                    {
                        return false;
                    }
                    peerBase.AppId = applicationName;
                    peerBase.CustomInitData = custom;
                    return peerBase.Connect(serverAddress, applicationName);
                }
            }
        }

        public virtual void Disconnect()
        {
            lock (dispatchLockObject)
            {
                lock (sendOutgoingLockObject)
                {
                    peerBase.Disconnect();
                }
            }
        }

        public virtual void Service()
        {
            while (DispatchIncomingCommands())
            {
            }
            while (SendOutgoingCommands())
            {
            }
        }

        public virtual bool SendOutgoingCommands()
        {
            lock (sendOutgoingLockObject)
            {
                return peerBase.SendOutgoingCommands();
            }
        }

        public virtual bool DispatchIncomingCommands()
        {
            lock (dispatchLockObject)
            {
                return peerBase.DispatchIncomingCommands();
            }
        }

        public virtual bool SendOperation(short operationCode, Dictionary<byte, object> operationParameters, SendOptions sendOptions)
        {
            if (sendOptions.Encrypt && !IsEncryptionAvailable)
            {
                throw new ArgumentException("Can't use encryption yet. Exchange keys first.");
            }
            lock (enqueueLock)
            {
                return peerBase.EnqueueOperation(operationParameters, operationCode, sendOptions);
            }
        }

        public bool EstablishEncryption()
        {
            return peerBase.ExchangeKeysForEncryption(sendOutgoingLockObject);
        }

        private void CreatePeerBase()
        {
            switch (TransportProtocol)
            {
                case ConnectionProtocol.Http:
                    peerBase = new HttpPeer();
                    break;
                case ConnectionProtocol.Tcp:
                    peerBase = new TcpPeer();
                    break;
                case ConnectionProtocol.WebSocket:
                    peerBase = new TcpPeer();
                    break;
                default:
                    break;
            }
            if (peerBase == null)
            {
                throw new Exception("No PeerBase");
            }
            peerBase.wgPeer = this;
            peerBase.usedTransportProtocol = TransportProtocol;
        }

        private static ConnectionProtocol GetConnectionProtocol(string serverAddress)
        {
            int idx = serverAddress.IndexOf(':');
            string head = serverAddress.Substring(0, idx);
            if (head == "http")
            {
                return ConnectionProtocol.Http;
            }
            else if (head == "https")
            {
                return ConnectionProtocol.HttpSecure;
            }
            else if (head == "tcp")
            {
                return ConnectionProtocol.Tcp;
            }
            else if (head == "ws")
            {
                return ConnectionProtocol.WebSocket;
            }
            else if (head == "wss")
            {
                return ConnectionProtocol.WebSocketSecure;
            }
            else
            {
                throw new InvalidOperationException(string.Format("ConnectionProtocol error! url={0}", serverAddress));
            }
        }
    }
}
